
#ifdef VEDA_OS_LINUX
#include <unistd.h>
#include <sys/time.h>
#include <sys/times.h>
#endif
#ifdef VEDA_OS_WIN32
#include <Windows.h>
#endif
#include <stdlib.h>
#include <time.h>
#include <string.h>

#include "VInfra/VDefine.h"
#include "Time.h"

namespace{

#ifdef VEDA_OS_LINUX
const static int TicksPerSecond = sysconf(_SC_CLK_TCK);
const clock_t UpTimeTicks = times(NULL);
static struct timespec UpTimeSpec = {0};
const static int Ret = clock_gettime(CLOCK_MONOTONIC, &UpTimeSpec);
const static Veda::int64 UpTimeMicroseconds = (Veda::int64)UpTimeSpec.tv_sec*1000000 + (Veda::int64)UpTimeSpec.tv_nsec/1000;
#endif
#ifdef VEDA_OS_WIN32
const static DWORD UpTimeMilliseconds = GetTickCount();
static LARGE_INTEGER TicksPerSecond;
const static BOOL Ret1 = QueryPerformanceFrequency(&TicksPerSecond);
const static double TicksPerMicrosecond = (double)(TicksPerSecond.QuadPart)/1000000;
static LARGE_INTEGER UpTimeTicks;
const static BOOL Ret2 = QueryPerformanceCounter(&UpTimeTicks);
const static Veda::int64 UpTimeMicroseconds = (Veda::int64)((double)(UpTimeTicks.QuadPart)/TicksPerMicrosecond);
#endif

const static char* WeekString1[] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
const static char* WeekString2[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
const static char* MonthString1[] = {"January","February","March","April","May","June","July","August","September","October","November","December"};
const static char* MonthString2[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

}

namespace Veda{

VTime::VTime(int64 t)
{
	time_t tm_utc = 0;
	t == 0?tm_utc = time(NULL):tm_utc = (time_t)t;
	utc = (int64)tm_utc;
	
	struct tm r = {0};
#ifdef VEDA_OS_LINUX
	localtime_r(&tm_utc, &r);
#endif
#ifdef VEDA_OS_WIN32
	localtime_s(&r, &tm_utc);
#endif
	second = r.tm_sec;
	minute = r.tm_min;
	hour = r.tm_hour;
	day = r.tm_mday;
	month = r.tm_mon+1;
	year = r.tm_year+1900;
	week = r.tm_wday;
}

VTime::VTime(int y, int M, int d, int h, int m, int s)
{
	struct tm t = {0};
	t.tm_sec = s>59?59:(s<0?0:s);
	t.tm_min = m>59?59:(m<0?0:m);
	t.tm_hour = h>23?23:(h<0?0:h);
	t.tm_mday = d>31?31:(d<0?0:d);
	t.tm_mon = M>12?11:(M<1?1:M-1);
	t.tm_year = y>2038?138:(y<1900?0:y-1900);
	time_t tm_utc = mktime(&t);
	utc = (int64)tm_utc;
	
	struct tm r = {0};
#ifdef VEDA_OS_LINUX
	localtime_r(&tm_utc, &r);
#endif
#ifdef VEDA_OS_WIN32
	localtime_s(&r, &tm_utc);
#endif
	second = r.tm_sec;
	minute = r.tm_min;
	hour = r.tm_hour;
	day = r.tm_mday;
	month = r.tm_mon+1;
	year = r.tm_year+1900;
	week = r.tm_wday;
}

VTime::VTime(const VTime& t)
{
	second = t.second;
	minute = t.minute;
	hour = t.hour;
	day = t.day;
	month = t.month;
	year = t.year;
	utc = t.utc;
	week = t.week;
}

VTime::~VTime()
{
}

VTime::operator int64() const
{
	return utc;
}

VTime& VTime::operator = (const int64& t)
{
	VTime tmp(t);
	*this = tmp;
  	return *this;
}

VTime& VTime::operator = (const VTime& t)
{
	second = t.second;
	minute = t.minute;
	hour = t.hour;
	day = t.day;
	month = t.month;
	year = t.year;
	utc = t.utc;
	week = t.week;
  	return *this;
}

bool VTime::operator == (const VTime& t) const
{
	return (utc == t.utc);
}

bool VTime::operator != (const VTime& t) const
{
	return (utc != t.utc);
}

bool VTime::operator > (const VTime& t) const
{
	return (utc > t.utc);
}

bool VTime::operator < (const VTime& t) const
{
	return (utc < t.utc);
}

bool VTime::operator >= (const VTime& t) const
{
	return (utc >= t.utc);
}

bool VTime::operator <= (const VTime& t) const
{
	return (utc <= t.utc);
}

bool VTime::format(char* buf, const char* format) const
{
	if(!buf || !format)
	{
		return false;
	}
	int i, j;
	for(i=0,j=0; *(format+i) != '\0'; i++)
	{
		if(*(format+i) == '#' && *(format+i+1) != '\0')
		{
			i++;
			switch(*(format+i))
			{
				case 'y':
				{
					int tmp = year;
					*(buf+j) = (char)(tmp/1000+'0');tmp=tmp%1000;j++;
					*(buf+j) = (char)(tmp/100+'0');tmp=tmp%100;j++;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				case 'Y':
				{
					int tmp = year%100;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				case 'M':
				{
					int tmp = month;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				case 'c':
				{
					int tmp = month;
					memcpy(buf+j, MonthString1[tmp-1], strlen(MonthString1[tmp-1]));j+=strlen(MonthString1[tmp-1]);
					break;
				}
				case 'C':
				{
					int tmp = month;
					memcpy(buf+j, MonthString2[tmp-1], 3);j+=3;
					break;
				}
				case 'd':
				{
					int tmp = day;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				case 'w':
				{
					int tmp = week;
					memcpy(buf+j, WeekString1[tmp], strlen(WeekString1[tmp]));j+=strlen(WeekString1[tmp]);
					break;
				}
				case 'W':
				{
					int tmp = week;
					memcpy(buf+j, WeekString2[tmp], 3);j+=3;
					break;
				}
				case 'h':
				{
					int tmp = hour;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				case 'H':
				{
					int tmp = hour%12;
					tmp = tmp==0?12:tmp;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				case 'n':
				{
					int tmp = hour;
					*(buf+j) = (tmp < 12)?'A':'P';j++;
					*(buf+j) = 'M';j++;
					break;
				}
				case 'm':
				{
					int tmp = minute;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				case 's':
				{
					int tmp = second;
					*(buf+j) = (char)(tmp/10+'0');tmp=tmp%10;j++;
					*(buf+j) = (char)(tmp+'0');j++;
					break;
				}
				default:
				{
					i--;
					*(buf+j) = '#';j++;
					break;
				}
			}
		}
		else
		{
			*(buf+j) = *(format+i);j++;
		}
	}
	*(buf+j) = '\0';
	return true;
}

void VTime::sleep(int sec)
{
#ifdef VEDA_OS_LINUX
	::sleep(sec);
#endif
#ifdef VEDA_OS_WIN32
	::Sleep(sec*1000);
#endif
}

void VTime::msleep(int milli)
{
#ifdef VEDA_OS_LINUX
	::usleep(milli*1000);
#endif
#ifdef VEDA_OS_WIN32
	::Sleep(milli);
#endif
}

void VTime::usleep(int micro)
{
#ifdef VEDA_OS_LINUX
	::usleep(micro);
#endif
#ifdef VEDA_OS_WIN32
	::Sleep(micro/1000);
#endif
}

bool VTime::setCurrentTime(const VTime& t, int tolerance)
{
	if(abs((int)(getCurrentUTCTime()-t.utc)) <= abs(tolerance))
	{
		return false;
	}
#ifdef VEDA_OS_LINUX
	struct timeval tv = {0};
	tv.tv_sec = t.utc;
	tv.tv_usec = 0;
	if(settimeofday(&tv, NULL) == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
#endif
#ifdef VEDA_OS_WIN32
	SYSTEMTIME tv = {0};
	tv.wYear = t.year;
	tv.wMonth = t.month;
	tv.wDay = t.day;
	tv.wHour = t.hour;
	tv.wMinute = t.minute;
	tv.wSecond = t.second;
	if(SetLocalTime(&tv) != 0)
	{
		return true;
	}
	else
	{
		return false;
	}
#endif
}

VTime VTime::getCurrentTime()
{
	VTime t;
	return t;
}

int64 VTime::getCurrentUTCTime()
{
	return (int64)time(NULL);
}

int64 VTime::getSecond()
{
#ifdef VEDA_OS_LINUX
	return (int64)(times(NULL) - UpTimeTicks)/TicksPerSecond;
#endif
#ifdef VEDA_OS_WIN32
	return (int64)((GetTickCount() - UpTimeMilliseconds)/1000);
#endif
}

int64 VTime::getMillisecond()
{
#ifdef VEDA_OS_LINUX
	return ((int64)((times(NULL) - UpTimeTicks)*1000)/TicksPerSecond);
#endif
#ifdef VEDA_OS_WIN32
	return (int64)(GetTickCount() - UpTimeMilliseconds);
#endif
}

int64 VTime::getMicrosecond()
{
#ifdef VEDA_OS_LINUX
	struct timespec t;
	clock_gettime(CLOCK_MONOTONIC, &t);
	return ((int64)t.tv_sec*1000000 + (int64)t.tv_nsec/1000 - UpTimeMicroseconds);
#endif
#ifdef VEDA_OS_WIN32
	LARGE_INTEGER t;
	QueryPerformanceCounter(&t);
	return ((Veda::int64)((double)(t.QuadPart)/TicksPerMicrosecond) - UpTimeMicroseconds);
#endif
}


}

